package biolabs.space;


/**
 * Represents a position in a 3-dimensional euclidean space.
 * That is a vector from
 * {@latex.inline $\\mathbb{R} \\times \\mathbb{R} \\times \\mathbb{R}$},
 * where {@latex.inline $\\mathbb{R}$} is the set of real numbers.
 *
 * @author Tomas Mikula
 */
public class Continuous3DPosition
		extends ContinuousPosition<Continuous3DPosition, Discrete3DPosition>
		implements Comparable<Continuous3DPosition> {

	public static final Continuous3DPosition ZERO = new Continuous3DPosition(0, 0, 0);
	public static final Continuous3DPosition ONE = new Continuous3DPosition(1, 1, 1);

	private final double x;
	private final double y;
	private final double z;

	public Continuous3DPosition(double x, double y, double z){
		this.x = x;
		this.y = y;
		this.z = z;
	}

	public double getX(){
		return x;
	}

	public double getY(){
		return y;
	}

	public double getZ(){
		return z;
	}

	@Override
	public Continuous3DPosition one() {
		return ONE;
	}

	@Override
	public Continuous3DPosition zero() {
		return ZERO;
	}

	@Override
	public boolean isEqualTo(Continuous3DPosition p){
		return x==p.x && y==p.y && z==p.z;
	}

	@Override
	public int compareTo(Continuous3DPosition p) {
		if(x < p.x)
			return -1;
		if(x > p.x)
			return 1;
		if(y < p.y)
			return -1;
		if(y > p.y)
			return 1;
		if(z < p.z)
			return -1;
		if(z > p.z)
			return 1;
		return 0;
	}

	@Override
	public String toString(){
		return "("+x+','+y+','+z+')';
	}

	@Override
	public Double innerProduct(Continuous3DPosition a) {
		return x*a.x + y*a.y + z*a.z;
	}

	@Override
	public double length() {
		return Math.sqrt(x*x + y*y + z*z);
	}

	@Override
	public Continuous3DPosition minus(Continuous3DPosition a) {
		return new Continuous3DPosition(x-a.x, y-a.y, z-a.z);
	}

	@Override
	public Continuous3DPosition plus(Continuous3DPosition a) {
		return new Continuous3DPosition(x+a.x, y+a.y, z+a.z);
	}

	@Override
	public Continuous3DPosition times(Double k) {
		return new Continuous3DPosition(k*x, k*y, k*z);
	}

	@Override
	public Continuous3DPosition divide(double k) {
		if(k==0)
			throw new IllegalArgumentException("Requested division by zero");
		return new Continuous3DPosition(x/k, y/k, z/k);
	}

	@Override
	public Continuous3DPosition normalized() {
		if(isEqualTo(ZERO))
			return this;
		return divide(length());
	}

	@Override
	public Discrete3DPosition rounded() {
		return new Discrete3DPosition((int)Math.round(x), (int)Math.round(y), (int)Math.round(z));
	}

	@Override
	public Double getCoordinate(int i) {
		switch(i){
		case 0: return x;
		case 1: return y;
		case 2: return z;
		default: throw new IndexOutOfBoundsException(i+"is an invalid index into a 3D vector");
		}
	}

	@Override
	public int getDimension() {
		return 3;
	}

	@Override
	public Continuous3DPosition withChangedCoordinate(int idx, Double value)
			throws IndexOutOfBoundsException {
		switch(idx){
		case 2: return new Continuous3DPosition(x, y, value);
		case 1: return new Continuous3DPosition(x, value, z);
		case 0: return new Continuous3DPosition(value, y, z);
		default: throw new IndexOutOfBoundsException(idx+"is an invalid index into a 3D vector");
		}
	}

}
